package dancrawler.server;

import dancrawler.common.Misc;
import dancrawler.proto.cmd.CmdHandlerRegister;
import dancrawler.server.events.DisconnectHandler;
import dancrawler.server.events.EventDirector;
import dancrawler.server.events.NewConnectionHandler;
import dancrawler.server.events.PlayerDeathHandler;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.BlockingQueue;

/**
 *
 */
public class GameHandler extends Thread {

    private Gameworld gw;
    private int ticks = 1;
    private BlockingQueue<netMsg> msgQueue;
    private BlockingQueue<netMsg> outQueue;
    private GameObjectRegister register;
    private CmdHandlerRegister cmdhr;
    private Server server;
    private boolean stop = false;
    private EventDirector evdir = new EventDirector();
    private Map<String, Player> players;
    private Map<Integer, Player> playerIds = new HashMap<>();
    private int nextPlayerId = 1;
    private Map<String, Integer> tilesIds = new HashMap<>();
    private int nextTileId = 1;
    private AI ai;
    private List<Move> updates = new ArrayList<>();
    private Player host = null;

    public GameHandler(Gameworld world, Server server, GameObjectRegister reg, CmdHandlerRegister ch) {
        register = reg;
        ai = new AI(this);
        this.server = server;
        gw = world;

        msgQueue = server.getInQueue();
        players = new Hashtable<>();
        cmdhr = ch;
        evdir.addEventHandler(new NewConnectionHandler(server));
        evdir.addEventHandler(new DisconnectHandler(server, this));
        evdir.addEventHandler(new PlayerDeathHandler(this, null));

    }

    public AI getAIController() {
        return ai;
    }

    public PlayerChar getPlayerCharacter(String id) {
        return players.get(id).getPc();
    }

    public Player getPlayer(String id) {
        return players.get(id);
    }

    public void setTopLevel(Gameworld gw) {
        this.gw = gw;
        gw.setUpdateList(updates);
    }

    public void changePlayerName(Player player, String newn) {
        players.remove(player.getCon().getId());
        server.changeClientName(player.getCon().getId(), newn);
        players.put(newn, player);

    }

    public GameObject addPlayer(String id) {

        Player pl = new Player(server.getClientConnection(id), null, nextPlayerId++);
        if(host == null)
        {
            host = pl;
        }
        PlayerChar newPlayer = new PlayerChar("danborgeS", gw, this, pl);
        pl.setPc(newPlayer);
        register.registerGameObject(newPlayer);
        playerIds.put(pl.getId(), pl);
        gw.getStartTile().add(newPlayer);
        gw.incPlayers();
        players.put(id, pl);
        return newPlayer;
    }

    public void removePlayer(Player pl) {
        players.remove(pl.getCon().getId());
    }

    @Override
    public void run() {
        while (!stop) {
            tick();
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
            }
        }
    }

    public void StopGame() {
        this.stop = true;
    }

    public EventDirector getEventDirector() {
        return evdir;
    }

    public Collection<Player> getPlayers() {
        return players.values();
    }

    public void tick() {
        int s = msgQueue.size();
        ArrayList<netMsg> msgList = new ArrayList<>(s);
        msgQueue.drainTo(msgList, s);


        evdir.processEvents();
        for (netMsg msg : msgList) {
            cmdhr.lookupHandler(msg.getCmd()).handleCmd(msg);
        }
        ai.think();
        for (Move t : updates) {
            netMsg nmsg;
            if (t.getTo() == null) {//Object is removed
                nmsg = new netMsg((short) 10, (short) 0, null);
                byte[][] args = new byte[1][];
                args[0] = Misc.shortToBytes((short) t.getObj().getId());
                nmsg.setArgs(args);
            } else {//object has moved

                nmsg = new netMsg((short) 1, (short) 0, null);
                byte[][] args = new byte[3][];
                args[0] = Misc.shortToBytes((short) t.getTo().getX());
                args[1] = Misc.shortToBytes((short) t.getTo().getY());
                args[2] = Misc.shortToBytes((short) t.getObj().getId());
                nmsg.setArgs(args);
            }
            nmsg.setFinalized(true);
            Collection<Connection> cons = server.getConnections();
            for (Player p : players.values()) {
                if (p.getPc().getWorld() == t.getObj().getWorld()
                        && p.getPc().getWorld() == t.getFrom().getWorld()) {
                    p.getCon().getOutQueue().offer(nmsg);
                }
            }

        }
        gw.clearUpdates();

    }

    public void queueMsg() {
    }

    public Gameworld getTopLevel() {
        return gw;
    }

    public BlockingQueue<netMsg> getOutQueue() {
        return outQueue;
    }

    public void sendWorld(Connection con) {
        netMsg msg = new netMsg((short) 24, (short) 0, con);
        con.getOutQueue().offer(msg);
        for (int i = 0; i < getPlayer(con.getId()).getPc().getWorld().getMapWidth(); i++) {
            for (int j = 0; j < getPlayer(con.getId()).getPc().getWorld().getMapHeight(); j++) {
                sendTile(getPlayer(con.getId()).getPc().getWorld().getTile(i, j), con);
            }
        }
    }

    public void sendTile(Tile t, Connection con) {
        if (t == null) {
            return;
        }
        if (!tilesIds.containsKey(t.getType())) {
            int id = nextTileId;
            tilesIds.put(t.getType(), nextTileId++);
            netMsg msg = new netMsg((short) 21, (short) 0, con);
            byte[][] args = new byte[2][];
            args[0] = Misc.shortToBytes((short) id);
            args[1] = Misc.stringToBytes((t.getType()));
            msg.setArgs(args);
            msg.setFinalized(true);
            con.getOutQueue().offer(msg);

        }
        netMsg msgo = new netMsg((short) 8, (short) 0, null);
        //msgo.setTarget(con);
        byte[][] args = new byte[3][];
        msgo.setArgs(args);
        args[0] = Misc.shortToBytes((short) t.getX());
        args[1] = Misc.shortToBytes((short) t.getY());
        args[2] = Misc.shortToBytes((short) tilesIds.get(t.getType()).intValue());
        msgo.setFinalized(true);
        con.getOutQueue().offer(msgo);
        for (GameObject go : t.getContents()) {
            msgo = new netMsg((short) 6, (short) 0, null);
            //msgo.setTarget(imsg.getSender());
            args = new byte[4][];
            msgo.setArgs(args);
            args[0] = Misc.shortToBytes((short) t.getX());
            args[1] = Misc.shortToBytes((short) t.getY());
            args[2] = Misc.stringToBytes(go.getType());
            args[3] = Misc.shortToBytes((short) go.getId());
            System.out.println("chow " + go.getId());
            msgo.setFinalized(true);
            con.getOutQueue().offer(msgo);
        }
    }

    public Set<Entry<String, Integer>> getTileIds() {
        return tilesIds.entrySet();
    }
    public void emptyWorld(Gameworld w)
    {
        if(w==getTopLevel())
        {

        }
    }
}
